use std::collections::HashMap;

use model_graph_types::container::workflow::{
    CliStatementBuilder, DataOperatorStatementBuilder, DocumentExtractorStatementBuilder,
    HtmlExtractorStatementBuilder, HttpStatementBuilder, ListOperatorStatementBuilder,
    LlmStatementBuilder, MetaOperatorStatementBuilder, PromptSpeechType, ScriptStatementBuilder,
    SpeechGeneratorStatementBuilder, SqlStatementBuilder, VariableAggregatorStatementBuilder,
    WaitStatementBuilder, WasmStatementBuilder, WorkflowBlock, WorkflowBlockBody,
    WorkflowBlockBuilder, WorkflowBlockForEachBuilder, WorkflowBlockStatement,
    WorkflowGraphNodeData,
};

pub async fn compile(id: &String, node: &WorkflowGraphNodeData) -> anyhow::Result<WorkflowBlock> {
    match node {
        WorkflowGraphNodeData::Start(v) => {
            return crate::workflow::blocks::start::compile(id, v).await;
        }
        WorkflowGraphNodeData::IfElse(v) => {
            return crate::workflow::blocks::if_else::compile(id, v).await;
        }
        WorkflowGraphNodeData::Code(v) => {
            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }

            //代码
            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::Script(
                        ScriptStatementBuilder::default()
                            .language(v.code_language.clone())
                            .script(&v.code)
                            .variables(v.variables.clone())
                            .outputs(outputs)
                            .build()?,
                    ),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::CLI(v) => {
            let mut builder = CliStatementBuilder::default();
            builder
                .arguments(v.arguments.clone())
                .command(v.command.clone());

            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }

            builder.outputs(outputs);

            if let Some(host) = &v.host {
                builder.host(host.clone());
            }

            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }

            builder.outputs(outputs);
            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::CLI(
                    builder.build()?,
                )))
                .build()?);
        }
        WorkflowGraphNodeData::Iteration(v) => {
            let mut builder = WorkflowBlockForEachBuilder::default();

            builder
                .output(v.output_selector.clone())
                .is_parallel(v.is_parallel)
                .parallel_nums(v.parallel_nums);

            if v.iterator_selector.len() == 2 {
                let first = v
                    .iterator_selector
                    .get(0)
                    .map_or(String::new(), |v| v.clone());
                let second = v
                    .iterator_selector
                    .get(1)
                    .map_or(String::new(), |v| v.clone());
                builder.iterator((first, second));
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::ForEach(builder.build()?))
                .build()?);
        }
        WorkflowGraphNodeData::IterationStart(v) => {
            //
            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::Empty))
                .build()?);
        }
        WorkflowGraphNodeData::HttpRequest(v) => {
            let mut builder = HttpStatementBuilder::default();

            builder
                .url(&v.url)
                .method(v.method.clone())
                .authorization(v.authorization.clone())
                .body(v.body.clone());

            if let Some(timeout) = &v.timeout {
                builder.timeout(timeout.clone());
            }
            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            let mut statement = builder.build()?;
            //headers
            if let Some(headers_str) = &v.headers {
                //
                statement.headers_from_string(headers_str)?;
            }
            //params
            if let Some(params_str) = &v.params {
                //
                statement.params_from_string(params_str)?;
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::Http(
                    statement,
                )))
                .build()?);
        }
        WorkflowGraphNodeData::LLM(v) => {
            let mut builder = LlmStatementBuilder::default();
            builder
                .model(v.model.clone())
                .prompt_template(v.prompt_template.clone());

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::LLM(
                    builder.build()?,
                )))
                .build()?);
        }
        WorkflowGraphNodeData::Wait(v) => {
            let mut builder = WaitStatementBuilder::default();

            if let Some(cases) = &v.cases {
                builder.cases(cases.clone());
            }

            if let Some(timeout) = &v.timeout {
                builder.timeout(timeout.clone());
            }

            if let Some(interval) = &v.interval {
                builder.interval(interval.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::Wait(
                    builder.build()?,
                )))
                .build()?);
        }
        WorkflowGraphNodeData::ListOperator(v) => {
            let mut builder = ListOperatorStatementBuilder::default();
            builder
                .variable(v.variable.clone())
                .var_type(v.var_type.clone())
                .filter_by(v.filter_by.clone())
                .limit(v.limit.clone())
                .extract_by(v.extract_by.clone())
                .order_by(v.order_by.clone())
                .item_var_type(v.item_var_type.clone());
            //
            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }
            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::ListOperator(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::VariableAggregator(v) => {
            let mut builder = VariableAggregatorStatementBuilder::default();
            builder
                .variables(v.variables.clone())
                .advanced_settings(v.advanced_settings.clone())
                .output_type(v.output_type.clone());
            //
            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }
            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::VariableAggregator(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::MetaOperator(v) => {
            let mut builder = MetaOperatorStatementBuilder::default();
            builder
                .class_type(v.class_type.clone())
                .identifier(v.identifier.clone())
                .method(v.method.clone());

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::MetaOperator(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::Wasm(v) => {
            let mut builder = WasmStatementBuilder::default();
            builder
                .file(v.file.clone())
                .function_name(v.function_name.clone());

            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }

            builder.outputs(outputs);

            if let Some(timeout) = &v.timeout {
                builder.timeout(timeout.clone());
            }

            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::Wasm(
                    builder.build()?,
                )))
                .build()?);
        }

        WorkflowGraphNodeData::HtmlExtractor(v) => {
            let mut builder = HtmlExtractorStatementBuilder::default();
            builder.url(v.url.clone()).selector(v.selector.clone());

            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }

            builder.outputs(outputs);

            if let Some(text) = &v.text {
                builder.text(*text);
            }

            if let Some(attribute) = &v.attribute {
                builder.attribute(attribute);
            }

            if let Some(timeout) = &v.timeout {
                builder.timeout(timeout.clone());
            }
            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::HtmlExtractor(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::DocumentExtractor(v) => {
            let mut builder = DocumentExtractorStatementBuilder::default();
            builder.variable_selector(v.variable_selector.clone());

            if let Some(is_array_file) = &v.is_array_file {
                builder.is_array_file(*is_array_file);
            }

            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::DocumentExtractor(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::SQLOperator(v) => {
            let mut builder = SqlStatementBuilder::default();
            builder
                .variables(v.variables.clone())
                .script(v.script.clone())
                .is_query(v.is_query.clone());

            if v.datasource.len() == 2 {
                let first = v.datasource.get(0).map_or(String::new(), |v| v.clone());
                let second = v.datasource.get(1).map_or(String::new(), |v| v.clone());
                builder.datasource((first, second));
            }
            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }
            builder.outputs(outputs);

            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(WorkflowBlockStatement::SQL(
                    builder.build()?,
                )))
                .build()?);
        }
        WorkflowGraphNodeData::DataOperator(v) => {
            let mut builder = DataOperatorStatementBuilder::default();
            builder
                .domain(v.domain.clone())
                .class(v.class.clone())
                .method(v.method.clone());

            let mut outputs = HashMap::new();

            for (k, v) in &v.outputs {
                outputs.insert(k.clone(), v.clone().try_into()?);
            }

            builder.outputs(outputs);

            if let Some(v) = &v.datasource {
                builder.datasource(v);
            }

            if let Some(v) = &v.conditions {
                builder.conditions(v.clone());
            }

            if let Some(v) = &v.extentions {
                builder.extentions(v.clone());
            }

            if let Some(v) = &v.value {
                builder.value(v.clone());
            }

            if let Some(v) = &v.options {
                builder.options(v.clone());
            }

            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::DataOperator(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::SpeechGenerator(v) => {
            let mut builder = SpeechGeneratorStatementBuilder::default();
            builder
                .model(v.model.clone())
                .text(v.text.clone())
                .prompt_speech_type(v.prompt_speech_type.clone());

            if let Some(speech) = &v.prompt_speech {
                builder.prompt_speech(speech.clone());
            }
            if let Some(speech) = &v.prompt_speech_selector {
                builder.prompt_speech_selector(speech.clone());
            }
            if let Some(speech) = &v.prompt_speech_template {
                builder.prompt_speech_template(speech.clone());
            }

            if let Some(error_strategy) = &v.error_strategy {
                builder.error_strategy(error_strategy.clone());
            }

            return Ok(WorkflowBlockBuilder::default()
                .id(id)
                .body(WorkflowBlockBody::Statement(
                    WorkflowBlockStatement::SpeechGenerator(builder.build()?),
                ))
                .build()?);
        }
        WorkflowGraphNodeData::Statement(v) => {
            return crate::workflow::blocks::statements::compile(id, v).await;
        }
        WorkflowGraphNodeData::End(v) => {
            return crate::workflow::blocks::return_statement::compile(id, v).await;
        }
        _ => {}
    }
    Err(anyhow::anyhow!("编译没有匹配到节点:{:?}", node))
}
